import re
import json
import csv
import pickle
from datetime import datetime

from abc import ABC, abstractmethod

class User(ABC):
    def __init__(self, user_id, name):
        self.user_id = user_id
        self.name = name

    @abstractmethod
    def login(self):
        pass

    @abstractmethod
    def logout(self):
        pass

class Student:
    def __init__(self, student_id, name, gender, ethnicity, phone_number):
        self.student_id = student_id
        self.name = name
        self.gender = gender
        self.ethnicity = ethnicity
        self.phone_number = phone_number
        self._dormitory_id = None

    @property
    def student_id(self):
        return self._student_id

    @student_id.setter
    def student_id(self, value):
        if not re.match(r"^\d{4}$", value):
            raise ValueError("学号必须是4位数字")
        self._student_id = value

    @staticmethod
    def validate_phone(phone_number):
        return re.match(r"^\d{11}$", phone_number) is not None

    def to_dict(self):
        return {
            "student_id": self.student_id,
            "name": self.name,
            "gender": self.gender,
            "ethnicity": self.ethnicity,
            "phone_number": self.phone_number,
            "dormitory_id": self._dormitory_id
        }

class Dormitory:
    def __init__(self, dormitory_id, capacity, gender=0):
        self.dormitory_id = dormitory_id
        self.capacity = capacity
        self.gender = gender
        self._students = []

    def add_student(self, student):
        if len(self._students) >= self.capacity:
            raise Exception("宿舍已满！")
        if self.gender != 0 and self.gender != student.gender:
            raise Exception("性别不符合宿舍规则！")
        self._students.append(student)

    def remove_student(self, student_id):
        self._students = [s for s in self._students if s.student_id != student_id]

    def is_full(self):
        return len(self._students) >= self.capacity

    def check_gender_rule(self, student):
        return self.gender == 0 or self.gender == student.gender

    def check_ethnicity_rule(self):
        ethnicities = set(s.ethnicity for s in self._students)
        return len(ethnicities) > 1

class Admin(User):
    def __init__(self, admin_id, name):
        super().__init__(admin_id, name)

    def login(self):
        print(f"管理员 {self.name} 登录成功！")

    def logout(self):
        print(f"管理员 {self.name} 登出成功！")

    def add_student(self, students, student):
        students.append(student)

    def modify_student(self, students, student_id, new_info):
        for s in students:
            if s.student_id == student_id:
                s.name = new_info.get("name", s.name)
                s.gender = new_info.get("gender", s.gender)
                s.ethnicity = new_info.get("ethnicity", s.ethnicity)
                s.phone_number = new_info.get("phone_number", s.phone_number)
                break

    def delete_student(self, students, student_id):
        students[:] = [s for s in students if s.student_id != student_id]

class Teacher(User):
    def __init__(self, teacher_id, name):
        super().__init__(teacher_id, name)

    def login(self):
        print(f"教师 {self.name} 登录成功！")

    def logout(self):
        print(f"教师 {self.name} 登出成功！")

    def query_dormitory(self, dormitories, dormitory_id):
        for d in dormitories:
            if d.dormitory_id == dormitory_id:
                return d._students
        return []

class Counselor(Teacher):
    def notify_students(self, students):
        for s in students:
            print(f"通知学生 {s.name} 宿舍安排已完成！")

class Log:
    log_id = 1

    def __init__(self, operation, operator_id):
        self.log_id = Log.log_id
        self.operation = operation
        self.operator_id = operator_id
        self.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        Log.log_id += 1

    def record_log(self):
        with open("logs.bin", "ab") as f:
            pickle.dump(self.__dict__, f)


def save_to_json(data, filename):
    with open(filename, "w", encoding="utf-8") as f:
        json.dump([s.to_dict() for s in data], f, ensure_ascii=False, indent=4)

def load_from_json(filename):
    try:
        with open(filename, "r", encoding="utf-8") as f:
            data = json.load(f)
            return [Student(**d) for d in data]
    except FileNotFoundError:
        return []

def main_menu():
    students = load_from_json("students.json")
    dormitories = load_from_json("dormitories.json")
    logs = []

    admin = Admin("admin001", "张三")
    admin.login()

    while True:
        print("\n请选择操作：")
        print("1、学生信息管理")
        print("2、宿舍信息管理")
        print("3、宿舍分配")
        print("N、退出系统")
        choice = input("请输入选项：").strip().lower()

        if choice == "1":
            student_management(students, logs)
        elif choice == "2":
            dormitory_management(dormitories, logs)
        elif choice == "3":
            assign_dormitory(students, dormitories, logs)
        elif choice == "n":
            break
        else:
            print("无效选项，请重新输入！")

    save_to_json(students, "students.json")
    save_to_json(dormitories, "dormitories.json")
    admin.logout()

def student_management(students, logs):
    while True:
        print("\n学生信息管理：")
        print("1、添加学生")
        print("2、修改学生信息")
        print("3、删除学生")
        print("4、查询学生信息")
        print("B、返回上一级")
        choice = input("请输入选项：").strip().lower()

        if choice == "1":
            student_id = input("请输入学号：")
            name = input("请输入姓名：")
            gender = int(input("请输入性别（1:男，2:女）："))
            ethnicity = int(input("请输入民族（1:汉，2:维吾尔，3:哈萨克...7:其他）："))
            phone_number = input("请输入手机号：")
            if not Student.validate_phone(phone_number):
                print("手机号格式错误！")
                continue
            student = Student(student_id, name, gender, ethnicity, phone_number)
            students.append(student)
            logs.append(Log("添加学生", "admin001"))
        elif choice == "b":
            break
        else:
            print("无效选项，请重新输入！")

if __name__ == "__main__":
    main_menu()